/**
  ******************************************************************************
  * @file    dcmi.c
  * @brief   This file provides code for the configuration
  *          of all used GPIO pins.
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "bsp.h"
#include "tskcfg.h"
#include "cmos.h"

volatile uint32_t Cmos_PhotoBuf[CMOS_PHOTOBUF_MAXLEN];

static void dcmi_dma_init(void)
{
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA2);
  
  LL_DMA_SetPeriphRequest(DMA2, LL_DMA_CHANNEL_5, LL_DMA_REQUEST_4);
  LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_CHANNEL_5, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
  LL_DMA_SetChannelPriorityLevel(DMA2, LL_DMA_CHANNEL_5, LL_DMA_PRIORITY_VERYHIGH);
  LL_DMA_SetMode(DMA2, LL_DMA_CHANNEL_5, LL_DMA_MODE_NORMAL);
  LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_CHANNEL_5, LL_DMA_PERIPH_NOINCREMENT);
  LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_CHANNEL_5, LL_DMA_MEMORY_INCREMENT);
  LL_DMA_SetPeriphSize(DMA2, LL_DMA_CHANNEL_5, LL_DMA_PDATAALIGN_WORD);
  LL_DMA_SetMemorySize(DMA2, LL_DMA_CHANNEL_5, LL_DMA_MDATAALIGN_WORD);
  LL_DMA_SetPeriphAddress(DMA2, LL_DMA_CHANNEL_5, (uint32_t) &(DCMI->DR));
  LL_DMA_SetMemoryAddress(DMA2, LL_DMA_CHANNEL_5, (uint32_t)Cmos_PhotoBuf);
  LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_5, CMOS_PHOTOBUF_MAXLEN);
  
  NVIC_SetPriority(DMA2_Channel5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
  NVIC_EnableIRQ(DMA2_Channel5_IRQn);
  
  LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_5);
  LL_DMA_EnableIT_TC(DMA2, LL_DMA_CHANNEL_5);
  
  WRITE_REG(DMA2->IFCR, DMA_IFCR_CGIF5|DMA_IFCR_CTCIF5|DMA_IFCR_CHTIF5|DMA_IFCR_CTEIF5);
  
//  LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_5);
//  LL_DMA_DisableChannel(DMA2, LL_DMA_CHANNEL_5);
}

void Dcmi_BspInit(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  
  /* DCMI clock enable */
  __HAL_RCC_DCMI_CLK_ENABLE();

  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  
  /**DCMI GPIO Configuration
  PE4     ------> DCMI_D4
  PE5     ------> DCMI_D6
  PE6     ------> DCMI_D7
  PA4     ------> DCMI_HSYNC
  PA6     ------> DCMI_PIXCLK
  PC6     ------> DCMI_D0
  PC7     ------> DCMI_D1
  PC8     ------> DCMI_D2
  PC9     ------> DCMI_D3
  PD3     ------> DCMI_D5
  PB7     ------> DCMI_VSYNC
  */
  GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF10_DCMI;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_4;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF10_DCMI;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_6;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF4_DCMI;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF10_DCMI;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF4_DCMI;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF4_DCMI;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF10_DCMI;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  
  dcmi_dma_init();
  
  /* DCMI interrupt Init */
  NVIC_SetPriority(DCMI_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), DCMI_IRQn_PRIORITY, 0));
  NVIC_EnableIRQ(DCMI_IRQn);
  
  CLEAR_REG(DCMI->CR);
  SET_BIT(DCMI->CR, DCMI_CR_CM | DCMI_CR_PCKPOL);//DCMI_CR_JPEG
  
  CLEAR_REG(DCMI->IER);
  SET_BIT(DCMI->IER, DCMI_IER_ERR_IE | DCMI_IER_OVR_IE | DCMI_IER_FRAME_IE);
}

void Dcmi_StartCapture(void)
{
  SET_BIT(DCMI->ICR, DCMI_ICR_FRAME_ISC|DCMI_ICR_OVR_ISC|DCMI_ICR_ERR_ISC|DCMI_ICR_VSYNC_ISC|DCMI_ICR_LINE_ISC);
  WRITE_REG(DMA2->IFCR, DMA_IFCR_CTCIF5|DMA_IFCR_CHTIF5|DMA_IFCR_CGIF5|DMA_IFCR_CTEIF5);
  
  LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_5, CMOS_PHOTOBUF_MAXLEN);
  
  LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_5);
  SET_BIT(DCMI->CR, DCMI_CR_ENABLE);
  SET_BIT(DCMI->CR, DCMI_CR_CAPTURE);
}

void Dcmi_StopCapture(void)
{
  CLEAR_BIT(DCMI->CR, DCMI_CR_ENABLE);
  CLEAR_BIT(DCMI->CR, DCMI_CR_CAPTURE);
  LL_DMA_DisableChannel(DMA2, LL_DMA_CHANNEL_5);
  
  SET_BIT(DCMI->ICR, DCMI_ICR_FRAME_ISC|DCMI_ICR_OVR_ISC|DCMI_ICR_ERR_ISC|DCMI_ICR_VSYNC_ISC|DCMI_ICR_LINE_ISC);
  WRITE_REG(DMA2->IFCR, DMA_IFCR_CTCIF5|DMA_IFCR_CHTIF5|DMA_IFCR_CGIF5|DMA_IFCR_CTEIF5);
}

void DCMI_IRQHandler(void)
{
  uint32_t misflags = READ_REG(DCMI->MISR);
  
  if((misflags & DCMI_MIS_FRAME_MIS) != 0x0u)
  {
    SET_BIT(DCMI->ICR, DCMI_ICR_FRAME_ISC);
    
    if(Cmos_Cmd_qhd != NULL)
    {
      xQueueSendFromISR(Cmos_Cmd_qhd, &Cmos_CmdCaptureComplete, NULL);
    }
    
    Dcmi_StopCapture();
  }
  
  if((misflags & DCMI_MIS_ERR_MIS) != 0x0u)
  {
    SET_BIT(DCMI->ICR, DCMI_ICR_ERR_ISC);
    
    if(Cmos_Cmd_qhd != NULL)
    {
      xQueueSendFromISR(Cmos_Cmd_qhd, &Cmos_CmdDcmiError, NULL);
    }
    
    Dcmi_StopCapture();
  }

  if((misflags & DCMI_MIS_OVR_MIS) != 0x0u)
  {
    SET_BIT(DCMI->ICR, DCMI_ICR_OVR_ISC);
    
    if(Cmos_Cmd_qhd != NULL)
    {
      xQueueSendFromISR(Cmos_Cmd_qhd, &Cmos_CmdDcmiOverrun, NULL);
    }
    
    Dcmi_StopCapture();
  }
}

void DMA2_Channel5_IRQHandler(void)
{
  if(LL_DMA_IsActiveFlag_TC5(DMA2) == 1)
  {
    WRITE_REG(DMA2->IFCR, DMA_IFCR_CTCIF5|DMA_IFCR_CHTIF5);
    
    if(Cmos_Cmd_qhd != NULL)
    {
      xQueueSendFromISR(Cmos_Cmd_qhd, &Cmos_CmdOverSize, NULL);
    }
    
    Dcmi_StopCapture();
  }
  
  if(LL_DMA_IsActiveFlag_TE5(DMA2) == 1)
  {
    WRITE_REG(DMA2->IFCR, DMA_IFCR_CTCIF5|DMA_IFCR_CHTIF5|DMA_IFCR_CGIF5|DMA_IFCR_CTEIF5);
    
    if(Cmos_Cmd_qhd != NULL)
    {
      xQueueSendFromISR(Cmos_Cmd_qhd, &Cmos_CmdTransferError, NULL);
    }
    
    Dcmi_StopCapture();
  }
}
